#include <dspSimulator/instruction/ext.h>


bool EXT::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void EXT::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                            std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    llvm::FunctionType *funcType = llvm::FunctionType::get(irBuilder->getVoidTy(), false);
    // ---------------------------------EXT (.unit) src2, csta, cstb, dst ---------------------------------------------
    {
        llvm::Function *extS = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "EXTKind1",
                                                      module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", extS);
        irBuilder->SetInsertPoint(entryBB);

        auto dstRegPtr = getDstLoRegPtr(module, irBuilder);

        auto src2RegPtr = getSrc2LoRegPtr(module, irBuilder);
        auto src2Reg = irBuilder->CreateLoad(src2RegPtr);

        auto cst5 = getCstN(module, irBuilder, 5);
        auto ucst5 = irBuilder->CreateZExt(cst5, irBuilder->getInt32Ty());

        auto auxCst5 = getAuxCstN(module, irBuilder, 5);
        auto auxUcst5 = irBuilder->CreateZExt(auxCst5, irBuilder->getInt32Ty());

        auto temp1 = irBuilder->CreateShl(src2Reg, ucst5);
        auto res = irBuilder->CreateAShr(temp1, auxUcst5);

        irBuilder->CreateStore(res, dstRegPtr);
        irBuilder->CreateRetVoid();
    }
}

std::shared_ptr<Instruction> EXT::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<EXT> ext;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<6>(data, 2, 0b010010) && existStandardCondition(data)) {
            ext = std::make_shared<EXT>(dspInstInfo->getAddress(), baseCycle);
            ext->creg_z = extractBits<4>(data, 28);
            ext->dst = extractBits<5>(data, 23).to_ulong();
            ext->src2 = extractBits<5>(data, 18).to_ulong();
            ext->cst = extractBits<5>(data, 13).to_ulong();
            ext->auxCst = extractBits<5>(data, 8).to_ulong();
            ext->s = data[1];
            ext->p = data[0];
            if (ext->s == 0) {
                ext->funcUnit = FuncUnit::S1;
            } else {
                ext->funcUnit = FuncUnit::S2;
            }
            ext->instFormat = InstFormat::Kind1;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (checkBits<3>(data, 10, 0b000) && checkBits<6>(data, 1, 0b110001)) {
            ext = std::make_shared<EXT>(dspInstInfo->getAddress(), baseCycle);
            ext->isCompactInstruction = true;
            ext->compactInstKind = "S2ext";
            ext->dst = extractBits<3>(data, 13).to_ulong();
            ext->src2 = extractBits<3>(data, 7).to_ulong();
            ext->cst = ext->auxCst = 16;
            ext->s = data[0];
            if (ext->s == 0) {
                ext->funcUnit = FuncUnit::S1;
            } else {
                ext->funcUnit = FuncUnit::S2;
            }
            ext->DSZ = dspInstInfo->getDSZ();
            ext->RS = dspInstInfo->getRS();
            ext->SAT = dspInstInfo->getSAT();
            ext->PROT = dspInstInfo->getPROT();
            ext->BR = dspInstInfo->getBR();
            ext->p = dspInstInfo->getP();
            ext->fixUpRegOnCompactInstruction();
            ext->instFormat = InstFormat::Kind1;
        } else if (checkBits<3>(data, 10, 0b010) && checkBits<6>(data, 1, 0b110001)) {
            ext = std::make_shared<EXT>(dspInstInfo->getAddress(), baseCycle);
            ext->isCompactInstruction = true;
            ext->compactInstKind = "S2ext";
            ext->dst = extractBits<3>(data, 13).to_ulong();
            ext->src2 = extractBits<3>(data, 7).to_ulong();
            ext->cst = ext->auxCst = 24;
            ext->s = data[0];
            if (ext->s == 0) {
                ext->funcUnit = FuncUnit::S1;
            } else {
                ext->funcUnit = FuncUnit::S2;
            }
            ext->DSZ = dspInstInfo->getDSZ();
            ext->RS = dspInstInfo->getRS();
            ext->SAT = dspInstInfo->getSAT();
            ext->PROT = dspInstInfo->getPROT();
            ext->BR = dspInstInfo->getBR();
            ext->p = dspInstInfo->getP();
            ext->fixUpRegOnCompactInstruction();
            ext->instFormat = InstFormat::Kind1;
        }
    }
    return ext;
}

void EXT::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {

}

std::string EXT::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    if (instFormat == InstFormat::Kind1) {
        res += getSrc2Reg32Name() + "," + std::to_string(cst) + "," + std::to_string(auxCst) +
               "," + getDstReg32Name();
    }
    return res;
}

std::shared_ptr<DspInstruction> EXT::clone() {
    return std::make_shared<EXT>(*this);
}
